var gLogNumber, gIncreaseNumber, gSetNumber;
// 这三个函数具有对同一个闭包的共享访问权限——这个闭包是指当三个函数定义时 setupSomeGlobals() 的局部变量
function setupSomeGlobals () {
    // 局部变量 num 最后会保存在闭包中
    var num = 42;
    // 将一些对于函数的引用存储为全局变量
    gLogNumber = function () {
        console.log(num)
    }
    gIncreaseNumber = function () {
        num++
    }
    gSetNumber = function (x) {
        num = x
    }
}
setupSomeGlobals()
gIncreaseNumber()
gLogNumber() // 43

gSetNumber(5)
gLogNumber() // 5

var oldLog = gLogNumber;
setupSomeGlobals()
gLogNumber() // 42
oldLog() // 5

// 注意：在上述例子中，当你再次调用 setupSomeGlobals()时，一个新的闭包就被创建了。
// 旧变量 gLogNumber, gIncreaseNumber, gSetNumber 被有新闭包的函数覆盖（在 JavaScript 中，如果你在一个函数中声明了一个新的函数，那么当外部函数被调用时，内部函数会被重新创建）

/**
 * 注意：当你在一个循环里面定义一个函数的时候，闭包里的局部变量可能不会像你想的那样。
 */
function buildList (list) {
    var result = []
    for (var i = 0; i < list.length; i++) {
        var item = 'item' + i
        result.push(function () {
            console.log(item + ' ' + list[i]);
        })
    }
    return result
}
function testList () {
    var fnlist = buildList([1,2,3])
    for (var j = 0; j < fnlist.length; j++) {
        fnlist[j]()
    }
}
testList() //输出 "item2 undefined" 3 次

/**
 * result.push( function() {console.log(item + ' ' + list[i])}这一行给result数组添加了三次函数匿名引用。
 * 
 * 和前面的示例一样，和 buildList 的局部变量对应的闭包只有一个。
 * 当匿名函数在 fnlist[j]() 这一行调用的时候，他们使用同一个闭包，而且是使用的这个闭包里 i 和 item 的值（循环结束后 i 的值为 3，item 的值为 'item2'）。
 * 
 * 注意：我们从索引0开始，所以item最后的值为item2'，i的值会被i++增加到3 。
 */

 /**
 * 这个例子表明了闭包会保存函数退出之前内部定义的所有的局部变量。
 * 注意：变量 alice 是在匿名函数之前创建的。
 * 匿名函数先被声明，然后当它被调用的时候之所以能够访问 alice 是因为他们在同一个作用域内（JavaScript 做了变量提升），sayAlice()() 直接调用了从 sayAlice() 中返回的函数引用——这个和前面的完全一样，只是少了临时的变量。（存储 sayAlice() 返回的函数引用的变量。）
 */

function sayAlice () {
    var say = function () {
        console.log(alice)
    }
    var alice = 'Hello Alice'
    return say
}
sayAlice()() // Hello Alice

/**
 * 技巧：需要注意变量say也是在闭包内部，也能被在sayAlice()内部声明的其它函数访问，或者也可以在函数内部递归访问它。
 */

/**
 * 最后一个例子说明了每次调用函数都会为局部变量创建一个闭包。实际上每次函数声明并不会创建一个单独的闭包，但每次调用函数都会创建一个独立的闭包。
 */

function newClosure (someNum, someRef) {
    // 局部变量最终保存在闭包中
    var num = someNum
    var anArray = [1,2,3]
    var ref = someRef

    return function (x) {
        num += x
        anArray.push(num)
        console.log('num: ' + num + '\nanArray ' + anArray.toString() + '\nref.someVar ' + ref.someVar);
    }
}
obj = {
    someVar: 4
}
fn1 = newClosure(4, obj)
fn2 = newClosure(5, obj)
fn1(1) // num: 5; anArray: 1,2,3,5; ref.someVar: 4;
fn2(1) // num: 6; anArray: 1,2,3,6; ref.someVar: 4;
obj.someVar++;
fn1(2); // num: 7; anArray: 1,2,3,5,7; ref.someVar: 5;
fn2(2); // num: 8; anArray: 1,2,3,6,8; ref.someVar: 5;